package com.itheima.auth.intergration.service;

import com.itheima.auth.intergration.authenticator.IntegrationAuthenticator;
import com.itheima.auth.intergration.entity.IntegrationAuthenticationEntity;
import com.itheima.auth.intergration.threadlocal.IntegrationAuthenticationHolder;
import com.itheima.common.exception.NcException;
import com.itheima.common.exception.enums.ResponseEnum;
import com.itheima.common.threadlocal.UserHolder;
import com.itheima.common.util.BeanHelper;
import com.itheima.common.util.JsonUtils;
import com.itheima.common.vo.UserInfo;
import com.itheima.sys.client.UserClient;
import com.itheima.sys.dto.CompanyUserDTO;
import com.itheima.sys.dto.SysRoleDTO;
import com.itheima.sys.dto.SysUserDTO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/**
 * 是security框架做认证时候自动调用MyUserDetailService中loadUserByUsername方法
 */

@Component
public class MyUserDetailService implements UserDetailsService {

    //认证器集合
    @Autowired
    private Collection<IntegrationAuthenticator> integrationAuthenticators;


    @Autowired
    private UserClient userClient;


    /**
     * 认证方法
     *
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //1.从ThreadLocal中获取认证相关参数
        IntegrationAuthenticationEntity entity = IntegrationAuthenticationHolder.get();
        String authType = entity.getAuthType();

        //2.通过不同认证方式参数名称 确定到底调用哪个认证器（用户名密码，短信验证码）
        IntegrationAuthenticator authenticate = this.chooseAuthenticate(entity);
        //调用不同认证器完成查询用户信息
        SysUserDTO sysUserDTO = authenticate.authenticate(entity);

        //3.返回当前认证用户信息 构造参数 1：认证主体 2：认证主体正确密码  3：当前用户权限 不能为空
        //设置固定权限
        UserInfo userInfo = BeanHelper.copyProperties(sysUserDTO, UserInfo.class);

        //获取用户提交企业ID-PC：当用户加入多个企业 用户选择其中一个进行登录  APP：记录上次登录企业id
        String companyId = entity.getAuthParameter("company_id");


        //4.获取企业ID，客户端类型参数
        String clientTypeId = entity.getAuthParameter("client_id");
        userInfo.setClientId(clientTypeId);
        //说明当前用户使用pc端进行登录-- 后台管理系统
        List<CompanyUserDTO> userDTOList = null;
        if ("pc_client".equals(clientTypeId)) {
            //5.远程调用系统微服务-获取当前用户员工列表记录-用于判断当前员工所在企业的角色是否为管理员
            userDTOList = userClient.queryCompanyUserById(sysUserDTO.getId(), null).getData();
            for (CompanyUserDTO companyUserDTO : userDTOList) {
                Boolean isAdmin = isAdmin(companyUserDTO);
                if(!isAdmin){
                    throw new NcException(ResponseEnum.USER_NOT_COMPANY_ADMIN);
                }
            }
            if(userDTOList.size()==1){
                //说明用户只加入一个企业-保证一次登录生成令牌中有企业信息
                CompanyUserDTO companyUserDTO = userDTOList.get(0);
                this.wrapperUserInfo(userInfo, companyUserDTO);
            }
            //6.远程调用系统微服务判断提交企业ID是否有值，如果有值查询企业用户记录，判断企业用户记录是否为管理员用户 将UserInfo进行封装
            if(StringUtils.isNotBlank(companyId)){
                //企业ID 用户ID确定一条员工记录 ： 管理员选择了其中企业进行登录
                userDTOList = userClient.queryCompanyUserById(sysUserDTO.getId(), Long.valueOf(companyId)).getData();
                //一定只有一个员工记录
                CompanyUserDTO companyUserDTO = userDTOList.get(0);
                //封装userInfo对象
                this.wrapperUserInfo(userInfo, companyUserDTO);
            }


        } else {
            //APP端认证
            //先判断是否提交companyId（上次登录企业ID）
            if(StringUtils.isBlank(companyId)){
                //获取用户表中记录上次登录企业ID
                companyId = sysUserDTO.getLastLoginCompanyId().toString();
            }
            //查询员工记录
            List<CompanyUserDTO> companyUserDTOList = userClient.queryCompanyUserById(sysUserDTO.getId(), Long.valueOf(companyId)).getData();
            if(!CollectionUtils.isEmpty(companyUserDTOList)){
                this.wrapperUserInfo(userInfo, companyUserDTOList.get(0));
            }
        }

        //将认证通过用户信息 存入ThreadLocal
        UserHolder.setUserInfo(userInfo);
        //判断用户信息中权限数据是否有值

        List<? extends GrantedAuthority> authorities = userInfo.getGrantedAuthorities() == null ? Arrays.asList(new SimpleGrantedAuthority("ROLE_TOURIST")) : userInfo.getGrantedAuthorities();
        User loginUser = new User(JsonUtils.toString(userInfo), sysUserDTO.getPassword(), authorities);
        return loginUser;
    }


    /**
     * 将查询到企业员工信息 给 校验通过后的用户包装对象赋值
     *
     * @param userInfo
     * @param companyUser
     * @return
     */
    public UserInfo wrapperUserInfo(UserInfo userInfo, CompanyUserDTO companyUser) {
        //将用户信息标识设置为企业员工ID
        userInfo.setId(companyUser.getId());
        userInfo.setCompanyId(companyUser.getCompanyId());
        userInfo.setCompanyName(companyUser.getCompanyName());
        userInfo.setDepartmentId(companyUser.getDepartmentId());
        userInfo.setDepartmentName(companyUser.getDepartmentName());
        userInfo.setPost(companyUser.getPost());
        userInfo.setWorkNumber(companyUser.getWorkNumber());
        if (!CollectionUtils.isEmpty(companyUser.getGrantedAuthorities())) {
            userInfo.setGrantedAuthorities(companyUser.getGrantedAuthorities());
        }
        return userInfo;
    }



    /*
    判断是否为管理员用户 ： 管理员用户：角色名称 ROLE_ADMIN_
     */
    private Boolean isAdmin(CompanyUserDTO companyUserDTO) {
        Boolean isAdmin = false;
        List<SysRoleDTO> roles = companyUserDTO.getRoles();
        for (SysRoleDTO role : roles) {
            if (role.getRoleName().startsWith("ROLE_ADMIN_")) {
                isAdmin = true;
                break;
            }
        }
        return isAdmin;
    }


    /**
     * 判断当前认证方式输入哪种认证方式
     *
     * @param entity
     * @return
     */
    public IntegrationAuthenticator chooseAuthenticate(IntegrationAuthenticationEntity entity) {
        for (IntegrationAuthenticator integrationAuthenticator : integrationAuthenticators) {
            if (integrationAuthenticator.support(entity)) {
                return integrationAuthenticator;
            }
        }
        return null;
    }

}
